Use _NET_WM_FRAME_DRAWN to synchronize frame drawing
authorOwen W. Taylor <otaylor@fishsoup.net>
Tue, 18 Sep 2012 13:37:03 +0000 (09:37 -0400)
committerOwen W. Taylor <otaylor@fishsoup.net>
Thu, 14 Feb 2013 22:19:48 +0000 (17:19 -0500)
As part of the extended _NET_WM_SYNC_REQUEST_COUNTER protocol,
we get a _NET_WM_FRAME_DRAWN message for each frame we draw. Use this
to synchronize the updates we are doing with the compositing manager's
drawing, and ultimately with with display refresh.

We now set the sync request counters on all windows, including
override-redirect windows, since it is also useful to do synchronized,
atomic updates for such windows.

https://bugzilla.gnome.org/show_bug.cgi?id=685460

gdk/x11/gdkdisplay-x11.c
gdk/x11/gdkwindow-x11.c
gdk/x11/gdkwindow-x11.h

index 5af36d278feaf7f37a7db5645c21bfb94f97250b..5afb11c417b34fea2818133122806ca98875daf7 100644 (file)
@@ -681,6 +681,13 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
                                              GDK_WINDOW_STATE_ICONIFIED);
             }
 
+          if (window_impl->toplevel &&
+              window_impl->toplevel->frame_pending)
+            {
+              window_impl->toplevel->frame_pending = FALSE;
+              gdk_frame_clock_thaw (gdk_window_get_frame_clock (event->any.window));
+            }
+
           _gdk_x11_window_grab_check_unmap (window, xevent->xany.serial);
         }
 
@@ -1132,6 +1139,21 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
       return GDK_FILTER_REMOVE;
     }
 
+  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
+    {
+      GdkWindowImplX11 *window_impl;
+
+      window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
+      if (window_impl->toplevel &&
+          window_impl->toplevel->frame_pending)
+        {
+          window_impl->toplevel->frame_pending = FALSE;
+          gdk_frame_clock_thaw (gdk_window_get_frame_clock (event->any.window));
+        }
+
+      return GDK_FILTER_REMOVE;
+    }
+
   return GDK_FILTER_CONTINUE;
 }
 
index 6b8a0e8fab3d8757ca74766921506e57df17ea7b..99d2f7f659779a4eaf877c82939e811cb5e3d5b5 100644 (file)
@@ -213,6 +213,51 @@ set_sync_counter(Display     *display,
     XSyncSetCounter(display, counter, sync_value);
 }
 
+static void
+gdk_x11_window_begin_frame (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (!WINDOW_IS_TOPLEVEL (window) ||
+      impl->toplevel->extended_update_counter == None)
+    return;
+
+  impl->toplevel->current_counter_value += 1;
+  set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+                  impl->toplevel->extended_update_counter,
+                  impl->toplevel->current_counter_value);
+}
+
+static void
+gdk_x11_window_end_frame (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (!WINDOW_IS_TOPLEVEL (window) ||
+      impl->toplevel->extended_update_counter == None)
+    return;
+
+  impl->toplevel->current_counter_value += 1;
+  set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+                  impl->toplevel->extended_update_counter,
+                  impl->toplevel->current_counter_value);
+
+  if (gdk_x11_screen_supports_net_wm_hint (gdk_window_get_screen (window),
+                                           gdk_atom_intern_static_string ("_NET_WM_FRAME_DRAWN")))
+    {
+      impl->toplevel->frame_pending = TRUE;
+      gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
+    }
+}
+
 /*****************************************************
  * X11 specific implementations of generic functions *
  *****************************************************/
@@ -616,9 +661,8 @@ ensure_sync_counter (GdkWindow *window)
     {
       GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
       GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
-      GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
 
-      if (toplevel && impl->use_synchronized_configure &&
+      if (toplevel &&
          toplevel->update_counter == None &&
          GDK_X11_DISPLAY (display)->use_sync)
        {
@@ -718,6 +762,20 @@ setup_toplevel_window (GdkWindow *window,
   ensure_sync_counter (window);
 }
 
+static void
+on_frame_clock_before_paint (GdkFrameClock *clock,
+                             GdkWindow     *window)
+{
+  gdk_x11_window_begin_frame (window);
+}
+
+static void
+on_frame_clock_after_paint (GdkFrameClock *clock,
+                            GdkWindow     *window)
+{
+  gdk_x11_window_end_frame (window);
+}
+
 void
 _gdk_x11_display_create_window_impl (GdkDisplay    *display,
                                      GdkWindow     *window,
@@ -878,6 +936,10 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
 
   clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
   gdk_window_set_frame_clock (window, clock);
+  g_signal_connect (clock, "before-paint",
+                    G_CALLBACK (on_frame_clock_before_paint), window);
+  g_signal_connect (clock, "after-paint",
+                    G_CALLBACK (on_frame_clock_after_paint), window);
 }
 
 static GdkEventMask
index b4b737604bcc0293d72e30d0760d6a0a2a4d81ff..c42365222c98da1a2ceefc10ed1265d5636e7dc8 100644 (file)
@@ -126,6 +126,9 @@ struct _GdkToplevelX11
   /* Set if the WM is presenting us as focused, i.e. with active decorations
    */
   guint have_focused : 1;
+
+  /* If we're expecting a response from the compositor after painting a frame */
+  guint frame_pending : 1;
   
   gulong map_serial;   /* Serial of last transition from unmapped */